#include "attri.h"

mempara* newMempara(const char* name, attri* val)
{
    mempara* ret = (mempara*)malloc(sizeof(mempara));
    assert(ret != NULL);
    ret->name = (char *)malloc((strlen(name) + 1) * sizeof(char));
    strcpy(ret->name, name);
    ret->val = val;
    ret->link = NULL;
    return ret;
}

mempara* copyMempara(mempara* src)
{
    mempara* ret = NULL;
    if(src){
        ret = newMempara(src->name,copyAttri(src->val));
        ret->link = copyMempara(src->link);
    }
    return ret;
}

void freeMempara(mempara* m)
{
    if(m){
        free(m->name);
        freeMempara(m->link);
        freeAttri(m->val);
        free(m);
    }
}


attri* newBaseAttri(int type)
{
    attri* ret = (attri*)malloc(sizeof(attri));
    assert(ret != NULL);
    ret->type = type;
    return ret;
}

attri* newArrayAttri(attri* subAttri, int size)
{
    attri* ret = newBaseAttri(ARRAY);
    ret->uarray.subAttri = subAttri;
    ret->uarray.size = size;
    return ret;
}

attri* newStructAttri(map_t* structDefMap)
{
    attri* ret = newBaseAttri(STRUCT);
    ret->ustruct.structDefMap = structDefMap;
    return ret;
}

attri* newStructDefAttri(mempara* memAttri)
{
    attri* ret = newBaseAttri(STRUCTDEF);
    ret->ustructDef.memAttri = memAttri;
    return ret;
}

attri* newFuncAttri(attri* retType, mempara* paraAttri)
{
    attri* ret = newBaseAttri(FUNC);
    ret->ufunc.retType = retType;
    ret->ufunc.paraAttri = paraAttri;
    int num = 0;
    mempara* tmp = paraAttri;
    while(tmp != NULL)
    {
        num++;tmp = tmp->link;
    }
    ret->ufunc.paraNum = num;
    return ret;
}

attri* copyAttri(attri* src)
{
    attri* ret = NULL;
    if (src) {
        switch (src->type) {
            case INT:
                ret = newBaseAttri(INT);
                break;
            case FLOAT:
                ret = newBaseAttri(FLOAT);
                break;
            case STRUCT:
                ret = newStructAttri(src->ustruct.structDefMap);
                break;
            case ARRAY:
                ret = newArrayAttri(copyAttri(src->uarray.subAttri),src->uarray.size);
                break;
            case STRUCTDEF:
                ret = newStructDefAttri(copyMempara(src->ustructDef.memAttri));
                break;
            case FUNC:
                ret = newFuncAttri(copyAttri(src->ufunc.retType),copyMempara(src->ufunc.paraAttri));
                break;
            default:
                assert(0);
        }
    }
    return ret;
}

void freeAttri(attri* a) {
    if (a) {
        switch (a->type) {
            case INT:
            case FLOAT:
            case STRUCT:
                break;
            case ARRAY:
                freeAttri(a->uarray.subAttri);
                break;
            case STRUCTDEF:
                freeMempara(a->ustructDef.memAttri);
                break;
            case FUNC:
                freeAttri(a->ufunc.retType);
                freeMempara(a->ufunc.paraAttri);
                break;
            default:
                assert(0);
        }
        free(a);
    }
}

char* typeNameArray[] = {
    "INT","FLOAT","ARRAY","STRUCT","STRUCTDEF","FUNC"
};

void printMempara(mempara* tmp)
{
    if(!tmp) return;
    printf("m or p name: %s\n",tmp->name);
    printf("m or p ");printAttri(tmp->val);
    printMempara(tmp->link);
}

void printAttri(attri* tmp)
{   if(!tmp) return;
    printf("type: %s\n",typeNameArray[tmp->type]);
    switch (tmp->type) {
            case INT:
            case FLOAT:
                break;
            case STRUCT:
                printf("structdef name: %s\n",tmp->ustruct.structDefMap->key);
                break;
            case ARRAY:
                printf("array size: %d\n",tmp->uarray.size);
                printAttri(tmp->uarray.subAttri);
                break;
            case STRUCTDEF:
                printMempara(tmp->ustructDef.memAttri);
                break;
            case FUNC:
                printf("ret ");
                printAttri(tmp->ufunc.retType);
                printf("paraNum: %d\n",tmp->ufunc.paraNum);
                printMempara(tmp->ufunc.paraAttri);
                break;
            default:
                assert(0);
        }
}

bool attriEqual(attri* t1, attri* t2) {
    if (!t1 || !t2) return false;
    if (t1->type != t2->type) return false;
    switch (t1->type) {
        case INT:
        case FLOAT:
            return true;
        case ARRAY:
            return attriEqual(t1->uarray.subAttri, t2->uarray.subAttri);
        case STRUCT:
            return strcmp(t1->ustruct.structDefMap->key, t2->ustruct.structDefMap->key) == 0;
        case FUNC:
            return attriEqual(t1->ufunc.retType, t2->ufunc.retType) &&
                   memparaEqual(t1->ufunc.paraAttri, t2->ufunc.paraAttri);
        default:
            assert(0);
    }
}

bool memparaEqual(mempara* mp1, mempara* mp2) {
    while (mp1 && mp2) {
        if (!attriEqual(mp1->val, mp2->val)) return false;
        mp1 = mp1->link;
        mp2 = mp2->link;
    }
    if (!mp1 && !mp2) return true;
    return false;
}